if(!QS) {
    var QS = {};
}
QS.DataHandler=function(cfg,callbacks){
	this.cfg={
		showAfter:true,
		gExchangeMarketTimeMap:{},
		pullSubSeparator:'_',
		pushSubSeparator:'|'
	};
	if(cfg){
		$.extend(true,this.cfg,cfg);
	}
	this.callbacks=callbacks||{};
    this.pause = false;
    this.connectionStatus=false;
    this.defaultModuleType='default';
    //this.type=type;
    this._id=-1;
    this.widgets = {};    // 0:whatchlist 1:chart 2:news 3:quicktake 4:alert 5:tickertape 6:l2MarketDepth 7:time&sales 8:options 10:quotes
	this.staticTokens=['tenforeCode','tenforeTicker','type','of028','os03s','os463','of009','sta0f','hs001','hs00l','sta4n','sta4j','sta4l','st159','os378','os388','os00m','st153','la03z','os060','os089','aa0a5','st168','st169','st109','st106','st200','st202','st201','st181','st206','st198','st263','st408','st415','sta44','pm032','sta65','pd003','pd005','pd007','pd009','pm493','pd00d','pd014','pd00f','pd00h','pm494','pm495','pd00b','st816','optiontype','rootsymbol','expirydate','strikeprice'];
	this.dynamicTokens={
		'AfterTDate':'s','AfterTTime':'i','AfterTPrice':'f','AfterTxt':'','AfterChg':'f','AfterChg%':'f',
		'TradeDate':'s','TradeTime':'i','LastPrice':'f','Chg':'f','Chg%':'f','LastMarket':'s',
		'OpenPrice':'f','HighPrice':'f','LowPrice':'f','ClosePrice':'f','MiddlePrice':'f',
		'AskMarket':'s','AskPrice':'f','AskSize':'i','BidMarket':'s','BidPrice':'f','BidSize':'i',
		'Date':'s','Time':'i','LastVolume':'i','Volume':'i','VWAP':'f','MarketCentre':'s','OpenInt':'s'
	};
	this.LA03Z={
    "$VAL_UMKNOWN$":"Not Classified","101":"Basic Materials","308":"Communication Services","102":"Consumer Cyclical",
    "205":"Consumer Defensive","309":"Energy","103":"Financial Services","206":"Healthcare",
    "310":"Industrials","104":"Real Estate","311":"Technology","207":"Utilities" };
	this.Mo = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', '']
	
	this.overrideToken={
		'Chg':'AfterChg',
		'Chg%':'AfterChg%',
		'TradeTime':'AfterTTime',
		'TradeDate':'AfterTDate',
		'Time':'AfterTTime',
		'Date':'AfterTDate',
		'LastPrice':'AfterTPrice',
		'ClosePrice':'LastPrice'
	};
	
	this.getSubSeparator=function(){
		return {
			pull:this.cfg.pullSubSeparator,
			push:this.cfg.pushSubSeparator
		}
	};
	
	var genDateTime=function(marketPriceMap,d){
		if(!marketPriceMap['Date']){// if package is not contains the date , will get it from cache.[gData]
			if(d['Date']&&d['Time']){
				marketPriceMap['Date']=d['Date'].replace(/-/g,'');
				marketPriceMap['Time']=d['Time'];
			}
		}
	};
	
    var  calculateChg=function(needUpdateField,d,securityType){
        var chg,ochg,chgp,ochgp;
        if(securityType=="20") {//forex  5 
            if(typeof needUpdateField['BidPrice']!='undefined'&&d['BidPrice']!=d['LastPrice']){
                needUpdateField['LastPrice']={
                    value:d['BidPrice'],
                    oValue:d['LastPrice'],
                    dataType:'f'
                };
                d['LastPrice']=d['BidPrice'];
            }   
            if(typeof needUpdateField['BidPrice']!='undefined'||typeof needUpdateField['ClosePrice']!='undefined'){
                if(typeof d['ClosePrice']!='undefined'&&typeof d['BidPrice']!='undefined'){
                    chg=d['BidPrice']-d['ClosePrice'];    
                    chgp=chg/d['ClosePrice']*100;
                    ochg=d['Chg'];
                    ochgp=d['Chg%'];
                    if (chg != ochg) {
                        d['Chg'] = chg;
                        d['Chg%'] = chgp;
                    }
                }
            }
            if(typeof needUpdateField['BidPrice']!='undefined'||typeof needUpdateField['AskPrice']!='undefined'){
                if(typeof d['BidPrice']!='undefined'&&typeof d['AskPrice']!='undefined'){
                    var sp=Math.abs(d['BidPrice']-d['AskPrice']);
                    if(sp!=d['Spread']){
                        needUpdateField['Spread']={
                            value:sp,
                            oValue:d['Spread'],
                            dataType:'f'
                        };
                        d['Spread']=sp;
                    }
                }
            }
        }else{
            if(d['showAfter'] == 1){
				if ((typeof needUpdateField['AfterTPrice']!='undefined'||typeof needUpdateField['LastPrice']!='undefined') && typeof d['LastPrice']!='undefined'&&typeof d['AfterTPrice']!='undefined') {
                    chg = d['AfterTPrice'] - d['LastPrice'];
                    chgp = chg / d['LastPrice'] * 100;
                    ochg = d['AfterChg'];
                    ochgp = d['AfterChg%'];
                    if (chg != ochg) {
                        d['AfterChg'] = chg;
                        d['AfterChg%'] = chgp;
                    }
                }
            }else{
                if ((typeof needUpdateField['LastPrice']!='undefined' || typeof needUpdateField['ClosePrice']!='undefined')&&typeof d['ClosePrice']!='undefined' && typeof d['LastPrice']!='undefined') { 
                    chg = d['LastPrice'] - d['ClosePrice'];
                    chgp = chg / d['ClosePrice'] * 100;
                    ochg = d['Chg'];
                    ochgp = d['Chg%'];
                    if (chg != ochg) {
                        d['Chg'] = chg;
                        d['Chg%'] = chgp;
                    }    
                }
            }
        }
        if (d['showAfter']==1) {
			if(typeof needUpdateField['LastPrice']!='undefined'){// if needupdateField contains LastPrice on pre-after, in fact, we need update Closeprice.
                needUpdateField['ClosePrice'] = needUpdateField['LastPrice'];
                delete needUpdateField['LastPrice'];
            }else{
                delete needUpdateField['ClosePrice'];
            }
			
            if(typeof needUpdateField['AfterTPrice']!='undefined'){//if needUpdateField contains LastPrice on pre-after, in fact, we need update LastPrice.
                needUpdateField['LastPrice'] = needUpdateField['AfterTPrice'];
                needUpdateField["LastPrice"].isAfter = true;
                delete needUpdateField['AfterTPrice'];
            }else{
                delete needUpdateField['LastPrice'];
            }
			
            if(typeof needUpdateField['AfterTTime']!='undefined'){
                needUpdateField['Time'] = needUpdateField['AfterTTime'];
                delete needUpdateField['AfterTTime'];
            }else{
                delete needUpdateField['Time'];
            }
            if(typeof needUpdateField['AfterTDate']!='undefined'){
                needUpdateField['Date'] = needUpdateField['AfterTDate'];
                delete needUpdateField['AfterTDate'];
            }else{
                delete needUpdateField['Date'];
            }
            if(typeof needUpdateField['AfterTxt']!='undefined'&&needUpdateField['AfterTxt'].value=='After Hours'){// first after price arrive. send the last price as previou close price.
                needUpdateField["ClosePrice"] = {
                    dataType: "f",
                    value: d["LastPrice"],
                    isAfter: true
                };
            }
        }else{
            deleteAfterData(needUpdateField);
        }
        if (chg != ochg) {
            needUpdateField['Chg'] = {
                value: chg,
                oValue: ochg,
                dataType: 'f'
            };
            needUpdateField['Chg%'] = {
                value: chgp,
                oValue: ochgp,
                dataType: 'f'
            };
            needUpdateField['Alert'] = {value: true,oValue: false,dataType:'b'};
        }
    };
    
    var deleteAfterData=function(map){
        delete map["AfterTDate"];
        delete map["AfterTTime"];
        delete map["AfterTPrice"];
    };
    
    var deleteTradeData=function(map){
        delete map["TradeDate"];
        delete map["TradeTime"];
        delete map["LastPrice"];
    };
    
    var setAfterTxt=function(cachedData,gExchangeMarketTimeMap,serviceName,gKey,time,needUpdateField){
        var txt='';
        if(parseInt(time,10) >= gExchangeMarketTimeMap[serviceName][1]*60+60){  // feed 3.0 change the time's unit to second.
            txt = "After Hours";
        }else if(parseInt(time,10) <= gExchangeMarketTimeMap[serviceName][0]*60){
            txt = "Pre-Market";
        }
        if(txt!=""&&txt!=cachedData[gKey]["AfterTxt"]){   // first extend hour price.
            needUpdateField["AfterTxt"]={value:txt,oValue:cachedData[gKey]["AfterTxt"],dataType:'s'};
            cachedData[gKey]["AfterTxt"]=txt;
        }
    };
	
	var formatDateString=function(str){
		var t = [];
		t.push(str.slice(0,4)),t.push(str.slice(4,6)),t.push(str.slice(6,8));
		return t.join("-");
	};
	
	var setAskBidMarket=function(marketPriceMap){
        if(!marketPriceMap["AskMarket"]&&(typeof marketPriceMap["AskPrice"]!='undefined'||typeof marketPriceMap["AskSize"]!='undefined')){
            marketPriceMap["AskMarket"] = marketPriceMap["LastMarket"];
        }
        if(!marketPriceMap["BidMarket"]&&(typeof marketPriceMap["BidPrice"]!='undefined'||typeof marketPriceMap["BidSize"]!='undefined')){
			marketPriceMap["BidMarket"] = marketPriceMap["LastMarket"];
        }
	};
	
	var returnExistFields=function(fields,marketPriceMap,subCacheData){
		var field0=fields[0],field1=fields[1],returnFields={};
		if( typeof marketPriceMap[field0]!='undefined' && typeof marketPriceMap[field1]!='undefined'){
			//format the price or size to float or int
			marketPriceMap[field0]=parseFloat(marketPriceMap[field0],10);
			marketPriceMap[field1]=parseInt(marketPriceMap[field1],10);
			
			returnFields={
				updateFields:{},
				storeFields:fields
			}
			
			//build the needUpdateFields
			if(marketPriceMap[field0]!=subCacheData[field0]){
				returnFields['updateFields'][field0]={
					value:marketPriceMap[field0],
					oValue:subCacheData[field0],
					dataType:'f'
				}
			}
			if(marketPriceMap[field1]!=subCacheData[field1]){
				returnFields['updateFields'][field1]={
					value:marketPriceMap[field1],
					oValue:subCacheData[field1],
					dataType:'i'
				}
			}
		
		}else if(typeof marketPriceMap[field0]!='undefined'){
			marketPriceMap[field0]=parseFloat(marketPriceMap[field0],10);
			returnFields={
				storeFields:[field0]
			}
			if(typeof subCacheData[field1] != 'undefined'){
				returnFields.updateFields={};
				if(marketPriceMap[field0]!=subCacheData[field0]){
					returnFields['updateFields'][field0]={
						value:marketPriceMap[field0],
						oValue:subCacheData[field0],
						dataType:'f'
					}
				}
				if(typeof subCacheData[field0] == 'undefined'){
					returnFields['updateFields'][field1]={
						value:subCacheData[field1],
						oValue:marketPriceMap[field1],
						dataType:'i'
					}
				}
			}
		}else if(typeof marketPriceMap[field1]!='undefined'){
			marketPriceMap[field1]=parseFloat(marketPriceMap[field1],10);
			returnFields={
				storeFields:[field1]
			}
			if(typeof subCacheData[field0] != 'undefined'){
				returnFields.updateFields={};
				if(marketPriceMap[field1]!=subCacheData[field1]){
					returnFields['updateFields'][field1]={
						value:marketPriceMap[field1],
						oValue:subCacheData[field1],
						dataType:'f'
					}
				}
				if(typeof subCacheData[field0] == 'undefined'){
					returnFields['updateFields'][field0]={
						value:subCacheData[field0],
						oValue:marketPriceMap[field0],
						dataType:'i'
					}
				}		
			}
		}
		return returnFields;
	};
	
	var handleAskBidData=function(cachedData,gkey,marketPriceMap,needUpdateField){
		if(!cachedData[gkey]["subExchangeAskBid"]){
            cachedData[gkey]["subExchangeAskBid"]={};
        }
		var am=marketPriceMap["AskMarket"],bm=marketPriceMap["BidMarket"];
		
		if(typeof am!='undefined'){
			if(!cachedData[gkey]["subExchangeAskBid"][am]){
				cachedData[gkey]["subExchangeAskBid"][am]={};
			}
			var subAskGData=cachedData[gkey]["subExchangeAskBid"][am];
			//check askprice/asksize paris of marketPriceMap
			var askFields=returnExistFields(['AskPrice','AskSize'],marketPriceMap,subAskGData);
			var updatedAskFields=askFields['updateFields']||[],storeAskFields=askFields['storeFields']||[];
			for(var fieldName in updatedAskFields){
				needUpdateField[fieldName]=updatedAskFields[fieldName];
				cachedData[gkey][fieldName]=updatedAskFields[fieldName]['value'];
				if(am!=cachedData[gkey]['AskMarket']){
					needUpdateField['AskMarket']={value:am,oValue:cachedData[gkey]['AskMarket'],dataType:'s'};
					cachedData[gkey]['AskMarket']=am;
				}
			}
			for(var i=0,l=storeAskFields.length;i<l;i++){
				var fieldName=storeAskFields[i];
				cachedData[gkey]["subExchangeAskBid"][am][fieldName]=marketPriceMap[fieldName];
			}
		}
		if(typeof bm!='undefined'){
			if(!cachedData[gkey]["subExchangeAskBid"][bm]){
				cachedData[gkey]["subExchangeAskBid"][bm]={};
			}
			var subBidGData=cachedData[gkey]["subExchangeAskBid"][bm];
			
			var bidFields=returnExistFields(['BidPrice','BidSize'],marketPriceMap,subBidGData);
			
			var updatedBidFields=bidFields['updateFields']||[],storeBidFields=bidFields['storeFields']||[];
			for(var fieldName in updatedBidFields){
				needUpdateField[fieldName]=updatedBidFields[fieldName];
				cachedData[gkey][fieldName]=updatedBidFields[fieldName]['value'];
				if(bm!=cachedData[gkey]['BidMarket']){
					needUpdateField['BidMarket']={value:bm,oValue:cachedData[gkey]['BidMarket'],dataType:'s'};
					cachedData[gkey]['BidMarket']=bm;
				}
			}
			for(var i=0,l=storeBidFields.length;i<l;i++){
				var fieldName=storeBidFields[i];
				cachedData[gkey]["subExchangeAskBid"][bm][fieldName]=marketPriceMap[fieldName];
			}
			
		}
	
	};
	/*
    this.updateChartWidget=function(gKey,dataType,marketPriceMap){
        var lastPrice=marketPriceMap["LastPrice"],tradeDate=marketPriceMap["TradeDate"],tradeTime=marketPriceMap["TradeTime"],volume=marketPriceMap["Volume"];
        if(lastPrice>0||volume>0){
            var needRefreshChartFields={
                LastPrice:{value:parseFloat(lastPrice,10),dataType:'f'},
                Volume:{value:parseInt(volume,10),dataType:'i'}
            };
            if(tradeDate){
                needRefreshChartFields["TradeDate"]={value:formatDateString(tradeDate),dataType:'s'};
            }
            if(tradeTime){
                needRefreshChartFields["Time"]={value:parseInt(tradeTime, 10),dataType:'i'};
            }
            if (((dataType=="R"&&marketPriceMap['LastMarket']!=17)||this.type=='push')&&!$.isEmptyObject(needRefreshChartFields)) {  // update chart component data. if source is R, lastMarket is 17 ,is BATS
                if(typeof GB_ChartDataManager!='undefined'){                                       // delayed pull data is from other interface.
                    GB_ChartDataManager.updateData(gKey,needRefreshChartFields);
                }        
            }
        }
    };*/
    
    this.getMarketQuoteData=function(gkey,field){  // get last price,chg%,chg,time and date from gData. 
        if(!gData[gkey])return null;
        var oData = gData[gkey],data = {};
        if(typeof field!='undefined'&&
           !(field=="LastPrice"||field=="Chg"||field=="ClosePrice"||field=="Chg%"||field=="Time"||field=="Date"||field=="R/D")){
            return {value: oData[field]};
        }
        if(oData["showAfter"]==1){   // show extend data,only relevant to showAfter.
            data["LastPrice"] = {dataType:"f",value:oData["AfterTPrice"],isAfter:true};
            data["Chg"] = {dataType:"f",value:oData["AfterChg"] }; //oData["AfterChg"];
            data["Chg%"] = {dataType:"f",value:oData["AfterChg%"] };  //oData["AfterChg%"];
            data["Time"] = {dataType:"f",value:oData["AfterTTime"] }; //oData["AfterTTime"];
            data["Date"] = {dataType:"f",value:oData["AfterTDate"] }; //oData["AfterTDate"];
            data["ClosePrice"]={dataType:"f",value:oData["LastPrice"] };//oData["LastPrice"];
        } else{
            data["LastPrice"] = {dataType:"f",value:oData["LastPrice"] };
            data["Chg"] = {dataType:"f",value:oData["Chg"] }; //oData["AfterChg"];
            data["Chg%"] = {dataType:"f",value:oData["Chg%"] };  //oData["AfterChg%"];
            data["Time"] = {dataType:"f",value:oData["Time"] }; //oData["AfterTTime"];
            data["Date"] = {dataType:"f",value:oData["Date"] }; //oData["AfterTDate"];
            data["ClosePrice"]={dataType:"f",value:oData["ClosePrice"] };//oData["LastPrice"];
        }
        data["showAfter"] = {dataType:"i",value:oData["showAfter"]};
        data["AfterTxt"] = {dataType:"s",value:oData["AfterTxt"]};
        data["R/D"] = {dataType:"s",value:oData["R/D"] };
        return typeof field == 'undefined'? data:data[field];
    };
    
	this._splitItemName=function(itemName){
		var aL = itemName.split(this.cfg.pushSubSeparator),tempAL;
		if(aL.length!=3){//the result is different between push and pull.
			tempAL=itemName.split(this.cfg.pullSubSeparator);
			if(tempAL.length==3){
				aL = tempAL;
			}else if(tempAL.length>3){// handle pull return 
				aL=[];
				aL[0]=tempAL[0];
				var temp=[],tempALLength =tempAL.length;
				for(var ti=1;ti<tempALLength-1;ti++){
					temp.push(tempAL[ti]);
				}
				aL[1]=temp.join(this.cfg.pullSubSeparator);
				aL[2]=tempAL[tempALLength-1];
			}
		}
		return aL;
	};
	
    this.updateMarketPrice=function(body,cachedData){
        var itemsReceived = body.items;
        var result = {}, aL, gKey, marketPriceMap, fieldsForItem;
        for (var i = 0, itemLh = itemsReceived.length; i < itemLh; i++) {
            result.sourceType = itemsReceived[i].source;
            result.serviceName = itemsReceived[i].service;
            result.itemName = itemsReceived[i].symbol;
            result.dataType = result.sourceType; // D or R.
            oExch = result.serviceName;
            //if (result.serviceName == "34" || result.serviceName == "17" || result.serviceName == "14" || result.serviceName == "19" || result.serviceName == "1") { // bats data 17.
            //    result.serviceName = "126";
            //}
			aL = this._splitItemName(result.itemName);

            result.symbolType = aL[0];
            result.itemName = aL[1];
            result.subcode = aL[2];
			if (result.serviceName < 126&&(result.symbolType==1||result.symbolType==10||result.symbolType==8)){  // USA index, stock etf and fund
				result.serviceName = "126";
			}
            fieldsForItem = itemsReceived[i].fields;
            gKey = result.serviceName + ":" + result.itemName;
            if(!cachedData[gKey]){
                cachedData[gKey]={};
            }
            marketPriceMap = {};
            var isAfter=null,AT=0,TT=0,DT=0,gTT=0,gAT=0,gDT=0;//AT:AfterTDate and Time,TT:TradeDate and Time,DT:Date and Timeg,gTT:TT in gData,gAT:AT in gData,gDT:DT in gData.
            marketPriceMap["R/D"] = result.dataType;
			
			//set LastMarket to sub-exchanges, beacause there is no LastMarket at sub-exchanges -- David 20120831 for Disnat
			marketPriceMap["LastMarket"] = oExch;
			
            for (var j = 0, len = fieldsForItem.length; j < len; j++) {
				if(fieldsForItem[j].name=='ClosePrice'&&fieldsForItem[j].value==0){// if closeprice is 0, we will ignore it.
					continue;
				}
                marketPriceMap[fieldsForItem[j].name] = fieldsForItem[j].value || '';
            }
			
            if (oExch == "17") {
                //marketPriceMap["LastMarket"] = "17";// bats data, set LastMarket to 17.it can be removed because we already unify to set lastmarket above.
                delete marketPriceMap["AfterTDate"];// if bats data,ignore after/pre
                delete marketPriceMap["AfterTTime"];
                delete marketPriceMap["AfterTPrice"];
            }
			
			if(oExch < 27 || oExch == 34){//volume is wrong when it is from sub-exchanges,we must delete .
				delete marketPriceMap["Volume"];
			}
			
			//reset ask/bid market by using lastmarket if they are not exist.
			setAskBidMarket(marketPriceMap);
            
            //step 1:check date time paris
            //remove Date/Time checking for SDK --- if ((typeof marketPriceMap['Date'] != 'undefined' ^ typeof marketPriceMap['Time'] != 'undefined')||
            if ((typeof marketPriceMap['TradeDate'] != 'undefined' ^ typeof marketPriceMap['TradeTime'] != 'undefined') ||
            (typeof marketPriceMap['AfterTDate'] != 'undefined' ^ typeof marketPriceMap['AfterTTime'] != 'undefined')) {
                continue;
            }
            var needUpdateField={};//,needRefreshChartFields={};
            gAT=cachedData[gKey]['AfterTDate']?parseInt((cachedData[gKey]['AfterTDate'].replace(/-/g,'')+cachedData[gKey]['AfterTTime']),10):0;// date :yyyy-mm-dd in gData
            gTT=cachedData[gKey]['TradeDate']?parseInt((cachedData[gKey]['TradeDate'].replace(/-/g,'')+cachedData[gKey]['TradeTime']),10):0;
			
			//compare if it is afterHour data
			if(marketPriceMap['AfterTDate'] &&marketPriceMap['TradeDate']){
				AT=parseInt((marketPriceMap['AfterTDate']+marketPriceMap['AfterTTime']),10);
                TT=parseInt((marketPriceMap['TradeDate']+marketPriceMap['TradeTime']),10);
				var atime,ttime,aflag=false;
				if(AT>=gAT){
					atime=AT;
					aflag=true;
				}else{
					atime=gAT;
					deleteAfterData(marketPriceMap);
				}
				if(TT>=gTT){
					ttime=TT;
				}else{
					ttime=gTT;
					deleteTradeData(marketPriceMap);
				}
				if(atime>ttime){
					isAfter=1;
					if(aflag) setAfterTxt(cachedData,this.cfg.gExchangeMarketTimeMap,result.serviceName,gKey,marketPriceMap['AfterTTime'],needUpdateField);
				}else{
					isAfter=0;
				}
			}else if(marketPriceMap['TradeDate']){
				TT=parseInt((marketPriceMap['TradeDate']+marketPriceMap['TradeTime']),10);
				if(TT>=gTT){
					if(TT>gAT){
						isAfter=0;
					}else{
						isAfter=1;//showAfter
					}
				}else{
					//deleteTradeData(marketPriceMap);
				}
			}else if(marketPriceMap['AfterTDate']){
				AT=parseInt((marketPriceMap['AfterTDate']+marketPriceMap['AfterTTime']),10);
				if(AT>=gAT){
					if(AT>gTT){
						isAfter=1;//showAfter
						setAfterTxt(cachedData,result.serviceName,gKey,marketPriceMap['AfterTTime'],needUpdateField);
					}else{
						isAfter=0;
					}
				}else{
					//deleteAfterData(marketPriceMap);
				}
								
			}
			
            
			if(this.cfg.showAfter){
				if(result.symbolType!=20&& isAfter!=null && isAfter!=cachedData[gKey]["showAfter"]){// if it is a forex ticker, showAfter is 0;
					needUpdateField["showAfter"]={value:isAfter,oValue:cachedData[gKey]["showAfter"],dataType:'i'};
					cachedData[gKey]["showAfter"]=isAfter;
				}
			}else{
				delete needUpdateField["AfterTxt"];
				cachedData[gKey]["showAfter"]=0;
			}
			
//          //solve ask/bid time is older than gdata
//          if(marketPriceMap["Time"]&&gData[gKey]["Date"]&&gData[gKey]["Time"]&(marketPriceMap["Date"]+marketPriceMap["Time"]<gData[gKey]["Date"].replace(/-/g,'')+gData[gKey]["Time"])){
//              marketPriceMap={};
//          }
            var closeIsLast=false,noneedfillgdata;
            for(var fieldName in marketPriceMap){
				ty='s';
				noneedfillgdata=false;
				switch(fieldName){
					case 'TradeDate':
					case 'Date':
					case 'AfterTDate':
						ty='s';
						marketPriceMap[fieldName] = formatDateString(marketPriceMap[fieldName]);
						break;
					case 'TradeTime':
					case 'LastVolume':
					case 'Time':
					case 'AfterTTime':
						ty='i';
						marketPriceMap[fieldName] = parseInt(marketPriceMap[fieldName], 10);
						break;
					case 'LastPrice':
					case 'ClosePrice':
						ty='f';
						var lastpricevalue=parseFloat(marketPriceMap[fieldName],10);
						if(lastpricevalue<=0 && (result.serviceName=="77"||result.serviceName=="75")&&result.symbolType=="3"){//77exchange and futrue
							closeIsLast=true;
						}
						marketPriceMap[fieldName] = lastpricevalue;
						break;
					case 'Volume':
						ty='i';
						marketPriceMap[fieldName] = parseFloat(marketPriceMap[fieldName], 10);
						needUpdateField['Alert'] = {value: true,oValue: false,dataType:'b'};
						break;
					case 'AskMarket':
					case 'BidMarket':
					case 'AskSize':
					case 'AskPrice':
					case 'BidSize':
					case 'BidPrice':
						noneedfillgdata=true;
						break;
					case 'Name':
						break;
					case 'MiddlePrice':
					case 'HighPrice':
					case 'LowPrice':
					case 'VWAP':
					case 'OpenPrice':
					case 'AfterTPrice':
						ty='f';
						marketPriceMap[fieldName] = parseFloat(marketPriceMap[fieldName],10);
						break;	
				}
				if(!noneedfillgdata&&marketPriceMap[fieldName]!=cachedData[gKey][fieldName]){
                    needUpdateField[fieldName]={value:marketPriceMap[fieldName],oValue:cachedData[gKey][fieldName],dataType:ty};
                    if(!closeIsLast){
                        cachedData[gKey][fieldName]=marketPriceMap[fieldName];
                    }
                }  			
            }

			//we need handle ask/bid as the same market
			handleAskBidData(cachedData,gKey,marketPriceMap,needUpdateField);
			
			if(needUpdateField["showAfter"]&&typeof needUpdateField["Date"]=='undefined'&&typeof cachedData[gKey]["Date"]!='undefined'){//if showAfter changed, we will force to update Date. to Fix QS-775
				needUpdateField["Date"]={value:cachedData[gKey]["Date"],oValue:null,dataType:'s'};
			}
			
            if(closeIsLast&&cachedData[gKey]["ClosePrice"]){
                if(cachedData[gKey]["ClosePrice"]!=cachedData[gKey]["LastPrice"]){
                    needUpdateField["LastPrice"] = {value:cachedData[gKey]["ClosePrice"],oValue:cachedData[gKey]["LastPrice"],dataType:"f"};
                    cachedData[gKey]["LastPrice"]=cachedData[gKey]["ClosePrice"];
                }else{
                    delete needUpdateField["LastPrice"];
                    cachedData[gKey]["LastPrice"]=cachedData[gKey]["ClosePrice"];
                }             
            }
			
            //step 5:cal chg/chg%
            calculateChg(needUpdateField,cachedData[gKey],result.symbolType);
            //step 6:update UI
			if(needUpdateField["Alert"]){
				needCheckAlert=true;
				delete needUpdateField["Alert"];
			}
            if (!$.isEmptyObject(needUpdateField) && typeof this.callbacks.onDataUpdate=='function') {
				this.callbacks.onDataUpdate(gKey, needUpdateField);
            }
        }
    };
    this.getWidget = function(type,id){
        if (this.widgets[type]&&this.widgets[type][id.toString()]) { 
            return this.widgets[type][id.toString()];
        } else {
            return {};
        }
    };
    this.getAllRegistedWidgets = function(type){
        return this.widgets[type];
    };
    this.register = function(obj) {
        if(typeof obj.type=='undefined'||obj.type==null){
            obj.type=this.defaultModuleType;
        }
        if(typeof obj.id=='undefined'||obj.id==null){
            obj.id=this._idServer(obj.type.toString());
        }
        if(!this.widgets[obj.type.toString()]){
            this.widgets[obj.type.toString()]={};
        }
        this.widgets[obj.type.toString()][obj.id] = obj;
    };

    this.unregister = function(obj) {
         delete this.widgets[obj.type.toString()][obj.id];
    };
    
    this._idServer=function(type){
        var id=this._id--;
        return type+"_"+id;
    };
	
	var handleAftTokensAsUpdateField=function(dynamicTokens,overrideToken,d,isNeedRemoved){
		var needUpdateField={};
		if(d.showAfter==1){
			for(k in overrideToken){
				var nk=overrideToken[k];
				if(typeof d[nk]=='undefined')continue;
				needUpdateField[k]={
					value:d[nk],
					oValue:null,
					dataType:dynamicTokens[k]
				}
				if(isNeedRemoved){
					delete dynamicTokens[nk];
				}
			}
			
		}else{
			if(isNeedRemoved){
				delete dynamicTokens['AfterTxt'];
				delete dynamicTokens['AfterTDate'];
				delete dynamicTokens['AfterTTime'];
				delete dynamicTokens['AfterTPrice'];
			}
		}
		return needUpdateField;
	};
	
	var handleAftTokens=function(dynamicTokens,overrideToken,d,isNeedRemoved){
		var data={};
		if(d.showAfter==1){
			for(k in overrideToken){
				var nk=overrideToken[k];
				if(typeof d[nk]=='undefined')continue;
				data[k]=d[nk];
				if(isNeedRemoved){
					delete dynamicTokens[nk];
				}
			}
		}else{
			if(isNeedRemoved){
				delete dynamicTokens['AfterTxt'];
				delete dynamicTokens['AfterTDate'];
				delete dynamicTokens['AfterTTime'];
				delete dynamicTokens['AfterTPrice'];
			}
		}
		return data;
	};
	
	var getDynamicTokensCopy=function(dynamicTokens){
		var copy=$.extend(true,{},dynamicTokens);
		return copy;
	};
	
	this.forceUpdateQuoteData=function(component,tickerObjects,isNeedRemoved){
		if(typeof component.updateData!='function'){
			return;
		}
		isNeedRemoved=isNeedRemoved===false?isNeedRemoved:(this.cfg.showAfter===true?true:false);
		var cp=isNeedRemoved===true?getDynamicTokensCopy(this.dynamicTokens):this.dynamicTokens;
		var tickerObject,gkey;
		for(var s in tickerObjects){
			tickerObject=tickerObjects[s];
			//gkey=tickerObject.getGKey();
			gkey=tickerObject.getField('gkey');
			var gd=gData[gkey];
			if(typeof gd=='undefined')continue;			
			var needUpdateField=handleAftTokensAsUpdateField(cp,this.overrideToken,gd,isNeedRemoved);
			for(var k in cp){
				if(needUpdateField[k]) continue;
				if(typeof gd[k]!='undefined'){
					needUpdateField[k]={
						value:gd[k],
						oValue:null,
						dataType:cp[k]
					}
				}
			}
			if($.isEmptyObject(needUpdateField)){
				continue;
			}
			component.updateData(tickerObject,needUpdateField);
		}
	};
	
	this.getSNAPQuoteData=function(tickerObjects,pullObj,callback){
		var pullKeys=[],pullKey,eCode,type,tTicker,exchCode,tickerObject,snapQuoteData=[],indexMap={};
		for(var i=0,l=tickerObjects.length;i<l;i++){
			tickerObject=tickerObjects[i];
			if(tickerObject.getField('gkey')){
				eCode=tickerObject.getField('tenforeCode'),//getTenforeCode(),
					type=parseInt(tickerObject.getField('type'),10),
					tTicker=tickerObject.getField('tenforeTicker');
				if (((type==1||type==10||type==8||type==2)&& eCode<=126&&eCode != 50)) { // USA stock,MF,Options and index(use 126 as exchange to subscribe level I data.)
					if(eCode != 41&&eCode!=11&&eCode!=9&&eCode!=30&&eCode!=29&&eCode!=33){
						exchCode = "126"; // 11,41 exchange is removed from 126 exchange.
					}else{
						exchCode=eCode;
					}
				}else{
					exchCode=eCode;
				}
				pullKey=exchCode+"."+type+"."+tTicker;
				gkey=tickerObject.getField('gkey');
				
				pullKeys.push(pullKey);
				if(!indexMap[gkey]){
					indexMap[gkey]=[];
				}
				indexMap[gkey].push(i);
			}else{
				snapQuoteData[i]={};
			}
		}
		var self=this,
			isNeedRemoved=this.cfg.showAfter===true?false:true,
			cp=isNeedRemoved===true?getDynamicTokensCopy(this.dynamicTokens):this.dynamicTokens;
		pullObj.query(pullKeys,function(o){
			var cachedData={};
			self.updateMarketPrice(o,cachedData);
			for(var gkey in indexMap){
				var gd=cachedData[gkey];
				var qd=handleAftTokens(cp,self.overrideToken,gd,isNeedRemoved);
				for(var k in cp){
					if(qd[k]) continue;
					if(typeof gd[k]!='undefined'){
						qd[k]=gd[k];
					}
				}
				for(var i=0,l=indexMap[gkey].length;i<l;i++){
					snapQuoteData[indexMap[gkey][i]]=qd;
				}
			}
			if(typeof callback=='function'){
				callback(snapQuoteData);
			}
		});
	};
	
	this.getQuoteData=function(tickerObjects,callback){
		var t,
			gkey,
			quoteData=[],
			isNeedRemoved=this.cfg.showAfter===true?false:true,
			cp=isNeedRemoved===true?getDynamicTokensCopy(this.dynamicTokens):this.dynamicTokens;
		for(var i=0,l=tickerObjects.length;i<l;i++){
			t=tickerObjects[i];
			//gkey=t.getGKey();
			gkey=t.getField('gkey');
			if(gkey){
				var gd=gData[gkey];
				if(typeof gd=='undefined'){
					quoteData[i]={};
					continue;
				}					
				var qd=handleAftTokens(cp,this.overrideToken,gd,isNeedRemoved);
				for(var k in cp){
					if(qd[k]) continue;
					if(typeof gd[k]!='undefined'){
						qd[k]=gd[k];
					}
				}
				quoteData[i]=qd;
			}else{
				quoteData[i]={};
			}
		}
		if(typeof callback=='function'){
			callback(quoteData);
		}
	};
	
	var getStaticDataFromCache=function(staticData,mapIndex,mapSIdGkey,tokens){
		var d={};
		for(var sid in  mapSIdGkey){
			var gkey=mapSIdGkey[sid];
			var idx=mapIndex[sid];
			d[idx[0]]={};
			if(gkey&&gData[gkey]){
				for(var j=0,jl=tokens.length;j<jl;j++){
					var token=tokens[j];
					if(typeof gData[gkey][token]=='undefined')continue;
					d[idx[0]][token]=gData[gkey][token];
				}
			}
			if(idx.length>0){
				for(var i=0,l=idx.length;i<l;i++){
					staticData[idx[i]]=d[idx[0]];
				}
			}
		}
	};
	
	this._cacheStaticData=function(mapSIdGkey,d){
		var mkey,gkey,dr,ecode;
		for (var i = 0; i < d.length; i++) { // store static data to gData.
			mkey = d[i].secId;
			gkey=mapSIdGkey[mkey];
			if (!gkey) 
				continue;
			if (!gData[gkey]) 
				gData[gkey] = {};
			dr = gData[gkey];
			
			ecode = dr["tenforeCode"];
			for (var j in d[i]) {
					if(j=="pd001"||j=="pd003"||j=="pd005"||j=="pd007"||j=="pd009"||j=="pd00b"||j=="pd00d"||j=="pd00f"||
					   j=="pd00h"||j=="pd014"||j=="pm493"||j=="pm494"||j=="pm495"){  // convert to two decimal for return value.
					   dr[j] = isNaN(d[i][j])?"":parseFloat(d[i][j]).toFixed(2);
					}else{
						dr[j] = d[i][j];
					}
                }
                if (dr['rr01y']) { //merge fund's Rating to ST's st200
                    dr['st200'] = dr['rr01y'];
                }
				if(dr["mType"]!="FE"&&dr["mType"]!="FC"){
	                if (dr['os70c']) { //merge fund's 52 wk high to ST's ST168
	                    dr['st168'] = dr['os70c'];
	                }
	                if (dr['os70e']) { //merge fund's 52 wk low to ST's ST169
	                    dr['st169'] = dr['os70e'];
	                }
	                if (dr['os70d']) { //merge fund's 52 wk high(date) to ST's ST109
	                    dr['st109'] = dr['os70d'];
	                }
	                if (dr['os70f']) { //merge fund's 52 wk low(date) to ST's ST106
	                    dr['st106'] = dr['os70f'];
	                }
				}
                if(dr['la03z']){//map to key
                    dr['la03z'] = this.LA03Z[dr['la03z']] || '';
                }
                if (dr['os378']) { //map to string - david
                    dr['os378'] = this.Mo[dr['os378']-1] || '';
                }
                if (ecode == "27") { // MSCI indexs
                    dr["LastPrice"] = dr["os065"]==""?"":parseFloat(dr["os065"]); // daily price to last price.
                    dr["Chg%"] = dr["pd003"]==""?"":parseFloat(dr["pd003"]);
                    dr["Chg"]= (dr["LastPrice"]!=""&&dr["Chg%"]!="")?dr["LastPrice"]*dr["Chg%"]/100:"";
                }
				if(dr["mType"]=="ST"&&dr["currency"]=="GBP"){  // change the currency from GBP to GBX for stock in Lodon exchanges.
				   dr["currency"] = "GBX";
				   dr["st168"] = dr["st168"]*100;  // 52 w high
				   dr["st169"] = dr["st169"]*100;  // 52 w low;
				}
                dr["hasStaticData"] = true;
				
		}
	}
	
	this._handlerStaticData=function(staticData,mapIndex,mapSIdGkey,remoteData,callback){
		//var staticData=getStaticDataFromCache(cachedTickers,this.staticTokens);
		this._cacheStaticData(mapSIdGkey,remoteData);
		if(typeof callback=='function'){
			getStaticDataFromCache(staticData,mapIndex,mapSIdGkey,this.staticTokens);
			callback(staticData);
		}
	};
	
	
	/**
	**@para [QSAPI.Ticker,QSAPI.Ticker]
	*/
	this.getStaticData=function(tickerObjects,callback){
		var self=this;
		if(tickerObjects.length==0){
			callback([]);
		}else{
			var t,
				secIds = [],
				secId,
				gkey,
				uniqueSIds={},
				staticData=[],
				mapIndex={},
				mapSIdGkey={};
			for(var i=0,l=tickerObjects.length;i<l;i++){
				t=tickerObjects[i];
				//secId=t.getSecId();
				secId=t.getField('secId')||t.getField('performanceId');
				if(secId==null||secId==''){
					staticData[i]={};
				}else{
					if(!mapIndex[secId]){
						mapIndex[secId]=[];
					}
					mapIndex[secId].push(i);
					//gkey=t.getGKey();
					gkey=t.getField('gkey');
					mapSIdGkey[secId]=gkey;
					if(!uniqueSIds[secId]){
						uniqueSIds[secId]=true;
						if(gkey!=null && gData[gkey] && gData[gkey]["hasStaticData"]){
							continue;
						}
						secIds.push(secId);
					}
				}
			}
			
			if(secIds.length==0){
				self._handlerStaticData(staticData,mapIndex,mapSIdGkey,[],callback);
				return;
			}
			
			var maxCount = 100;
			if(secIds.length>maxCount){
				var subSecIdsStr = [];
				var c = Math.ceil(secIds.length / maxCount); 
				var cbNum = 0;
				var returnArray = [];
				for (var i = 0; i < c; i++) {
					subSecIdsStr.push(secIds.slice(maxCount * i, maxCount * (i + 1)).join(','));
					$.getJSON(this.cfg.staticDataService+"?callback=?",{'secId': subSecIdsStr[i]},function(o){
						cbNum++;
						returnArray = returnArray.concat(o.data||[]); //merge the return
						if (cbNum >= c) {
							self._handlerStaticData(staticData,mapIndex,mapSIdGkey,returnArray,callback);
						}
					});
					
				}
			}else{
				$.getJSON(this.cfg.staticDataService+"?callback=?",{'secId': secIds.join(',')},function(o){
						self._handlerStaticData(staticData,mapIndex,mapSIdGkey,o.data||[],callback);
					}
				);
			}
		}
	};
	
	this.destory=function(){
		
	};
};